//
// Created by mnutec on 2021/2/26.
//

#ifndef MY3D_RUBIKCUBE_H
#define MY3D_RUBIKCUBE_H
#include "GL.h"
using namespace GLM;

class ColorCube : public Cube
{
public:
    Texture *face[6];
    // 蓝橘白绿红黄
    //蓝菊绿红白黄
    void rotateY(int side)
    {
        if (side == 1)
        {
            swap(face[0], face[3]);
            swap(face[5], face[3]);
            swap(face[1], face[3]);
        }
        else
        {
            swap(face[0], face[5]);
            swap(face[3], face[5]);
            swap(face[1], face[5]);
        }
    }

    void rotateX(int side)
    {
        if (side == 1)
        {
            swap(face[0], face[4]);
            swap(face[2], face[4]);
            swap(face[1], face[4]);
        }
        else
        {
            swap(face[0], face[2]);
            swap(face[4], face[2]);
            swap(face[1], face[2]);
        }
    }

    void rotateZ(int side)
    {
        if (side == 1)
        {
            swap(face[2], face[5]);
            swap(face[3], face[5]);
            swap(face[4], face[5]);
        }
        else
        {
            swap(face[2], face[3]);
            swap(face[5], face[3]);
            swap(face[4], face[3]);
        }
    }
    void draw(Graphic &g)
    {

        g.loadMatrix(m);
        g.VertexPointer(&mesh);
        for (int i = 0; i < 6; i++)
        {
            g.loadTexture(*face[i]);
            g.drawIndexed(indices[i]);
        }
    }
};

class RubikCube
{
private:
    GLWindow* window;
    float delta = 0;
public:
    ColorCube cubes[3][3][3];
    
    Texture textures[7];
    bool isRotate;
    int rotateAxis;
    int rotateLayer;
    float oldThetaX[3] = { 0 }, newThetaX[3] = { 0 };
    float oldThetaY[3] = { 0 }, newThetaY[3] = { 0 };
    float oldThetaZ[3] = { 0 }, newThetaZ[3] = { 0 };
    
    int side;
    
    void RotateXLayer(int layer, float theta)
    {
        if (layer == 0)
            theta = -theta;
        Vector4 v1 = {1, 0, 0, 1};
        for (int y = 0; y < 3; y++)
        {
            for (int z = 0; z < 3; z++)
            {
                cubes[layer][y][z].rotate(theta, cubes[layer][1][1].m.apply(v1));
            }
        }
    }

    void RotateYLayer(int layer, float theta)
    {
        if (layer > 0)
            theta = -theta;
        Vector4 v1 = {0, 1, 0, 1};
        for (int x = 0; x < 3; x++)
        {
            for (int z = 0; z < 3; z++)
            {
                cubes[x][layer][z].rotate(theta, cubes[1][layer][1].m.apply(v1));
            }
        }
    }

    void RotateZLayer(int layer, float theta)
    {
        if (layer == 0)
            theta = -theta;
        Vector4 v1 = {0, 0, 1, 1};
        for (int x = 0; x < 3; x++)
        {
            for (int y = 0; y < 3; y++)
            {
                cubes[x][y][layer].rotate(theta, cubes[1][1][layer].m.apply(v1));
            }
        }
    }

    void resetPosition()
    {

        for (int x = 0; x < 3; x++)
        {

            for (int y = 0; y < 3; y++)
            {
                for (int z = 0; z < 3; z++)
                {
                    cubes[x][y][z].m.setIdentity();
                    cubes[x][y][z].translate({(x - 1) * 2.0f, (y - 1) * 2.0f, (z - 1) * 2.0f, 1});
                    //cubes[x][y][z].translate(cubePosition[j]*2);
                }
            }
        }
    }
    RubikCube(GLWindow* window){
        this->window=window;
    


        
    
        isRotate = false;

        cubePosition[0] = {0, -1, -1, 1};
        cubePosition[1] = {0, 0, -1, 1};
        cubePosition[2] = {0, 1, -1, 1};
        cubePosition[3] = {0, -1, 0, 1};
        cubePosition[4] = {0, -0, 0, 1};
        cubePosition[5] = {0, 1, 0, 1};
        cubePosition[6] = {0, -1, 1, 1};
        cubePosition[7] = {0, 0, 1, 1};
        cubePosition[8] = {0, 1, 1, 1};

        textures[0].loadFormFile("texture/Black.png");
        textures[1].loadFormFile("texture/Blue.png");
        textures[2].loadFormFile("texture/Orange.png");
        textures[3].loadFormFile("texture/White.png");
        textures[4].loadFormFile("texture/Green.png");
        textures[5].loadFormFile("texture/Red.png");
        textures[6].loadFormFile("texture/Yellow.png");

        for (int i = 0; i < 3; i++)
        {

            for (int j = 0; j < 3; j++)
            {

                for (int k = 0; k < 3; k++)
                {
                    cubes[i][j][k].face[0] = &textures[0];
                    cubes[i][j][k].face[1] = &textures[0];
                    cubes[i][j][k].face[2] = &textures[0];
                    cubes[i][j][k].face[3] = &textures[0];
                    cubes[i][j][k].face[4] = &textures[0];
                    cubes[i][j][k].face[5] = &textures[0];
                }
            }
        }
        for (int i = 0; i < 3; i++)
        {

            for (int j = 0; j < 3; j++)
            {

                //蓝菊绿红白黄
                cubes[i][j][2].face[0] = &textures[1];
                cubes[i][j][0].face[1] = &textures[2];

                cubes[j][2][i].face[4] = &textures[3];
                cubes[j][0][i].face[2] = &textures[4];

                cubes[2][i][j].face[3] = &textures[5];
                cubes[0][i][j].face[5] = &textures[6];
            }
        }

        resetPosition();
    }

    void draw(Graphic &g)
    {

        for (int x = 0; x < 3; x++)
        {

            for (int y = 0; y < 3; y++)
            {

                for (int z = 0; z < 3; z++)
                {
                    cubes[x][y][z].draw(g);
                }
            }
        }
    }

    void startRotate(int axis, int layer, int side)
    {
        if (isRotate)
            return;
        this->isRotate = true;
        this->rotateAxis = axis;
        this->rotateLayer = layer;
        this->side = side;
        if (axis == 0)
        {
            newThetaX[layer] += side * PI / 2;
        }

        if (axis == 1)
        {
            newThetaY[layer] += side * PI / 2;
        }

        if (axis == 2)
        {
            newThetaZ[layer] += side * PI / 2;
        }
    }

    bool isEqual(float a, float b,float delta)
    {
        
        return abs(a - b) <= delta*1.1;
        
    }

    void rotate(float delta)
    {
        
        

 

      
        resetPosition();
        
        
        float theta = 0;
        bool isOverRotate = false;
        if (isRotate)
        {
            switch (rotateAxis)
            {
            case 0:
            {
                theta = oldThetaX[rotateLayer] + side * delta*3;

                oldThetaX[rotateLayer] = theta;
                if (isEqual(oldThetaX[rotateLayer], newThetaX[rotateLayer],delta))
                {
                    newThetaX[rotateLayer] = oldThetaX[rotateLayer];
                    isRotate = false;
                    if (side == 1)
                    {
                        for (int y = 0; y < 3; y++)
                        {
                            for (int z = 0; z < y; z++)
                            {
                                swap(cubes[rotateLayer][y][z], cubes[rotateLayer][z][y]);
                            }
                        }
                    }
                    else
                    {
                        for (int y = 0; y < 3; y++)
                        {
                            for (int z = 1 - y; z >= 0; z--)
                            {
                                swap(cubes[rotateLayer][y][z], cubes[rotateLayer][2 - z][2 - y]);
                            }
                        }
                    }
                    for (int i = 0; i < 3; i++)
                    {
                        swap(cubes[rotateLayer][0][i], cubes[rotateLayer][2][i]);
                    }

                    isOverRotate = true;
                }
            }
            break;
            case 1:
            {
                theta = oldThetaY[rotateLayer] + side * delta*3;

                oldThetaY[rotateLayer] = theta;
                if (isEqual(oldThetaY[rotateLayer], newThetaY[rotateLayer],delta))
                {
                    newThetaY[rotateLayer] = oldThetaY[rotateLayer];

                    isRotate = false;
                    if (side == 1)
                    {
                        for (int x = 0; x < 3; x++)
                        {
                            for (int z = 0; z < x; z++)
                            {
                                swap(cubes[x][rotateLayer][z], cubes[z][rotateLayer][x]);
                            }
                        }
                    }
                    else
                    {
                        for (int x = 0; x < 3; x++)
                        {
                            for (int z = 1 - x; z >= 0; z--)
                            {
                                swap(cubes[x][rotateLayer][z], cubes[2 - z][rotateLayer][2 - x]);
                            }
                        }
                    }
                    for (int i = 0; i < 3; i++)
                    {
                        swap(cubes[0][rotateLayer][i], cubes[2][rotateLayer][i]);
                    }

                    isOverRotate = true;
                }
            }
            break;
            case 2:
            {
                theta = oldThetaZ[rotateLayer] + side* delta*3;

                oldThetaZ[rotateLayer] = theta;
                if (isEqual(oldThetaZ[rotateLayer], newThetaZ[rotateLayer],delta))
                {
                    newThetaZ[rotateLayer] = oldThetaZ[rotateLayer];

                    isRotate = false;
                    if (side == 1)
                    {
                        for (int x = 0; x < 3; x++)
                        {
                            for (int y = 0; y < x; y++)
                            {
                                swap(cubes[x][y][rotateLayer], cubes[y][x][rotateLayer]);
                            }
                        }
                    }
                    else
                    {
                        for (int x = 0; x < 3; x++)
                        {
                            for (int y = 1 - x; y >= 0; y--)
                            {
                                swap(cubes[x][y][rotateLayer], cubes[2 - y][2 - x][rotateLayer]);
                            }
                        }
                    }
                    for (int i = 0; i < 3; i++)
                    {

                        swap(cubes[0][i][rotateLayer], cubes[2][i][rotateLayer]);
                    }

                    isOverRotate = true;
                }
            }
            break;
            default:
                break;
            }
        }
        if (isOverRotate)
        {
            resetPosition();
            if (rotateAxis == 0)
            {
                for (int y = 0; y < 3; y++)
                {
                    for (int z = 0; z < 3; z++)
                    {
                        cubes[rotateLayer][y][z].rotateX(side);
                    }
                }
            }
            else if (rotateAxis == 1)
            {
                for (int x = 0; x < 3; x++)
                {
                    for (int z = 0; z < 3; z++)
                    {
                        cubes[x][rotateLayer][z].rotateY(side);
                    }
                }
            }
            else if (rotateAxis == 2)
            {
                for (int x = 0; x < 3; x++)
                {
                    for (int y = 0; y < 3; y++)
                    {
                        cubes[x][y][rotateLayer].rotateZ(side);
                    }
                }
            }
            for (int i = 0; i < 3; i++)
            {
                oldThetaX[i] = newThetaX[i] = 0;
                oldThetaY[i] = newThetaY[i] = 0;
                oldThetaZ[i] = newThetaZ[i] = 0;
            }
        }
        
        static float theta2 = 0;
        theta2 += delta;
        Vector4 v= {1.0f,1.0f,1.0f,1.0f};
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                for(int k=0;k<3;k++){
                    cubes[i][j][k].rotate(theta2,v);
                }
            }
        }
        for (int i = 0; i < 3; i++)
        {
            RotateXLayer(i, oldThetaX[i]);
            RotateYLayer(i, oldThetaY[i]);
            RotateZLayer(i, oldThetaZ[i]);
        }
        
        
    }

private:
    Vector4 cubePosition[9];
};

#endif //MY3D_RUBIKCUBE_H
